﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

namespace Jerry
{
    /// <summary>
    /// AssetBundle管理器
    /// </summary>
    public class JABMgr : SingletonMono<JABMgr>
    {
        #region 变量

        protected override void Awake()
        {
            base.Awake();
            DontDestroyOnLoad(this.gameObject);
        }

        #region 根Manifest

        /// <summary>
        /// 根Manifest，暂时不再使用
        /// </summary>
        static private AssetBundleManifest m_AssetBundleManifest = null;
        /// <summary>
        /// 是否使用Manifest文件
        /// </summary>
        private bool useManifest = false;

        /// <summary>
        /// <para>根Manifest</para>
        /// <para>AssetBundleManifest object which can be used to load the dependecies and check suitable assetBundle variants.</para>
        /// </summary>
        static public AssetBundleManifest AssetBundleManifestObject
        {
            set
            {
                m_AssetBundleManifest = value;
            }
            get
            {
                return m_AssetBundleManifest;
            }
        }

        #endregion 根Manifest

        /// <summary>
        /// 资源缓存，测试中
        /// </summary>
        private const bool m_SaveAssets = true;
        private Dictionary<string, UnityEngine.Object> m_LoadedAssets = new Dictionary<string, UnityEngine.Object>();

        /// <summary>
        /// 检查已经保存的资源，要在检查过参数之后使用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetBundleName"></param>
        /// <param name="assetName"></param>
        /// <param name="callback"></param>
        /// <returns>是否有保存</returns>
        private bool CheckSaveAssets<T>(string assetBundleName, string assetName, Action<T> callback = null) where T : UnityEngine.Object
        {
            string assetKey = RemapVariantName(assetBundleName) + "." + assetName;
            if (m_SaveAssets
                && m_LoadedAssets.ContainsKey(assetKey)
                && m_LoadedAssets[assetKey] != null
                && m_LoadedAssets[assetKey] is T)
            {
                if (!m_PauseLoading)
                {
                    if (callback != null)
                    {
                        T asset = m_LoadedAssets[assetKey] as T;
                        callback(asset);
                    }
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// 加载好缓存的AssetBundle
        /// </summary>
        private Dictionary<string, AssetBundle> m_LoadedAssetBundles = new Dictionary<string, AssetBundle>();

        /// <summary>
        /// 正在加载的资源
        /// </summary>
        private Dictionary<string, WWW> m_DownloadingWWWs = new Dictionary<string, WWW>();
        /// <summary>
        /// 排队加载的资源
        /// </summary>
        private List<ReadyLoadBundle> m_ReadyLoadBundles = new List<ReadyLoadBundle>();
        /// <summary>
        /// <para>同时加载Bundle的数量</para>
        /// </summary>
        private int m_SameTimeLoadBundleCnt = 10;
        /// <summary>
        /// 后台同时加载Bundle的数量，要小于等于m_SameTimeLoadBundleCnt
        /// </summary>
        private int m_BackgroundSameTimeLoadBundleCnt = 5;
        /// <summary>
        /// 是否允许后台加载，默认不允许
        /// </summary>
        private bool m_CanBackgroundLoading = false;
        /// <summary>
        /// 加载错误，AssetBundle2ErrorStr
        /// </summary>
        private Dictionary<string, string> m_DownloadingErrors = new Dictionary<string, string>();
        /// <summary>
        /// 操作队列
        /// </summary>
        private List<JABLoadOperation> m_InProgressOperations = new List<JABLoadOperation>();
        /// <summary>
        /// 依赖表
        /// </summary>
        private Dictionary<string, string[]> m_Dependencies = new Dictionary<string, string[]>();
        /// <summary>
        /// 服务器资源路径根目录
        /// </summary>
        private string BaseServerDownloadingURL = "";
        /// <summary>
        /// 本地资源路径根目录
        /// </summary>
        private string BaseLocalDownloadingURL = "";
        /// <summary>
        /// 本地资源管理器
        /// </summary>
        private BundleInfoMgr localBundleInfoMgr = null;
        public BundleInfoMgr LocalBundleInfoMgr
        {
            get
            {
                return localBundleInfoMgr;
            }
        }
        /// <summary>
        /// 服务器资源管理器，仅使用本地资源不会有
        /// </summary>
        private BundleInfoMgr serverBundleInfoMgr = null;

        /// <summary>
        /// 当前平台名，决定AB的Manifest文件名
        /// </summary>
        private string CurPlatformName = "UnKnow";

        /// <summary>
        /// 设定要使用的变体名
        /// </summary>
        private string[] m_ActiveVariants = { };

        /// <summary>
        /// Variants which is used to define the active variants.
        /// </summary>
        private string[] ActiveVariants
        {
            get { return m_ActiveVariants; }
            set { m_ActiveVariants = value; }
        }

        /// <summary>
        /// <para>Bundle加载完成回调</para>
        /// <para>参数:名称-是否成功</para>
        /// </summary>
        private Action<string, bool> m_BundleLoadedCallback = null;

        /// <summary>
        /// 待后台下载的资源列表
        /// </summary>
        private List<BundleInfo> m_BundleInfosToBackgroundLoad = new List<BundleInfo>();

        /// <summary>
        /// 只使用本地资源，也就是不做资源更新，从而也没有分包，主要供测试用，这样可以免去不断更新远程资源包
        /// </summary>
        private bool m_OnlyUseLocalRes = false;
        /// <summary>
        /// 暂停加载，暂停后的请求不再给回调
        /// </summary>
        private bool m_PauseLoading = false;
        /// <summary>
        /// 暂停加载
        /// </summary>
        public bool PauseLoading
        {
            get
            {
                return m_PauseLoading;
            }
            set
            {
                m_PauseLoading = value;
            }
        }

        #endregion 变量

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="platformName">平台名</param>
        /// <param name="streamingAssetsPath">首包资源地址</param>
        /// <param name="persistentDataPath">包外存储地址</param>
        /// <param name="afterJABMgrInit">回调，返回【初始化错误信息，没有表示成功】、【是否第一次解析（需要整理首包资源）】</param>
        /// <param name="onlyUseLocalRes">只用本地资源</param>
        /// <param name="serverABUrl">服务器资源地址，只用本地资源时不需要</param>
        /// <param name="sameTimeLoadBundleCnt">同时下载数量</param>
        /// <param name="backgroundSameTimeLoadBundleCnt">后台同时下载数量</param>
        public void Init(string platformName,
            string streamingAssetsPath,
            string persistentDataPath,
            Action<string, bool> afterJABMgrInit,
            bool onlyUseLocalRes = true,
            string serverABUrl = "",
            int sameTimeLoadBundleCnt = 10,
            int backgroundSameTimeLoadBundleCnt = 5)
        {
            Caching.maximumAvailableDiskSpace = 500 * 1024 * 1024;

            m_SameTimeLoadBundleCnt = sameTimeLoadBundleCnt;
            m_BackgroundSameTimeLoadBundleCnt = backgroundSameTimeLoadBundleCnt;
            m_OnlyUseLocalRes = onlyUseLocalRes;
            CurPlatformName = platformName;
            m_CanBackgroundLoading = false;

            JABMgr.Inst.m_BundleLoadedCallback -= BundleLoaded;
            JABMgr.Inst.m_BundleLoadedCallback += BundleLoaded;

            BaseServerDownloadingURL = serverABUrl + "/" + CurPlatformName + "/";
            BaseLocalDownloadingURL = streamingAssetsPath + "/" + CurPlatformName + "/";

            if (useManifest)
            {
                JABMgr.Inst.StartCoroutine(IE_LoadManifest((errInfo) =>
                {
                    LoadBundleInfoMgr(afterJABMgrInit, persistentDataPath, errInfo);
                }));
            }
            else
            {
                LoadBundleInfoMgr(afterJABMgrInit, persistentDataPath);
            }
        }

        private void LoadBundleInfoMgr(Action<string, bool> afterJABMgrInit, string persistentDataPath, string errInfo = "")
        {
            string errorInfo = errInfo;
            bool firstTime = false;
            if (!m_OnlyUseLocalRes)
            {
                LoadInfoListFromPath(BaseServerDownloadingURL + BundleInfoMgr.m_VersionFileName, (success) =>
                {
                    if (success)
                    {
                        LoadLocalBundleInfoMgr(persistentDataPath, ref firstTime, ref errorInfo);
                    }
                    else
                    {
                        errorInfo = "拉取远端版本文件失败";
                    }
                    if (afterJABMgrInit != null)
                    {
                        afterJABMgrInit(errorInfo, firstTime);
                    }
                });
            }
            else
            {
                LoadLocalBundleInfoMgr(persistentDataPath, ref firstTime, ref errorInfo);
                if (afterJABMgrInit != null)
                {
                    afterJABMgrInit(errorInfo, firstTime);
                }
            }
        }

        /// <summary>
        /// 加载本地版本文件
        /// </summary>
        /// <param name="persistentDataPath"></param>
        /// <param name="firstTime"></param>
        /// <param name="errorInfo"></param>
        private void LoadLocalBundleInfoMgr(string persistentDataPath, ref bool firstTime, ref string errorInfo)
        {
            string pdPath = persistentDataPath + "/" + BundleInfoMgr.m_VersionFileName;

            if (!File.Exists(pdPath))//第一次整理//注意：首包要解压完成后才能存档
            {
                firstTime = true;
                LoadInPackageLocalBundleInfoMgr(ref errorInfo);
            }
            else
            {
                try
                {
                    localBundleInfoMgr = JsonUtility.FromJson<BundleInfoMgr>(File.ReadAllText(pdPath));
                }
                catch (Exception ex)
                {
                    errorInfo += ex.Message;
                }

                //版本更新，覆盖安装，会保留旧数据
                if (localBundleInfoMgr != null && localBundleInfoMgr.m_Version.StartsWith(Application.version) == false)
                {
                    firstTime = true;
                    LoadInPackageLocalBundleInfoMgr(ref errorInfo);
                }
            }
            if (localBundleInfoMgr != null)
            {
                localBundleInfoMgr.SetPersistentDataPath(persistentDataPath);
            }
        }

        /// <summary>
        /// 加载包内的本地版本文件
        /// </summary>
        /// <param name="errorInfo"></param>
        private void LoadInPackageLocalBundleInfoMgr(ref string errorInfo)
        {
            string saPath = BundleInfoMgr.m_VersionFileShortName;
            TextAsset txt = Resources.Load<TextAsset>(saPath);
            if (txt != null)
            {
                try
                {
                    localBundleInfoMgr = JsonUtility.FromJson<BundleInfoMgr>(txt.text);
                }
                catch (Exception ex)
                {
                    errorInfo = ex.Message;
                }
            }
            else
            {
                errorInfo += "包内读取版本文件错误";
            }
        }

        #region 检查首包

        /// <summary>
        /// 检查首包
        /// </summary>
        /// <param name="isFirst">是否是第一次解析</param>
        /// <param name="refreshPro">刷新进度回调，参数：进度[0,1]-分子-分母</param>
        /// <param name="afterCheckFirstPackage">检查首包完成，参数是是否需要存档</param>
        public void CheckFirstPackage(bool isFirst, Action<float, long, long> refreshPro, Action<bool> afterCheckFirstPackage = null)
        {
            if (isFirst == false)
            {
                if (afterCheckFirstPackage != null)
                {
                    afterCheckFirstPackage(false);
                }
                return;
            }
            List<BundleInfo> bundleInfos = localBundleInfoMgr.InfoList;
            if (bundleInfos.Count <= 0)
            {
                LocalBundleInfoMgr.SaveData(true);
                if (afterCheckFirstPackage != null)
                {
                    afterCheckFirstPackage(false);
                }
                return;
            }
            this.StartCoroutine(IE_LoadBundles(bundleInfos, refreshPro, (hasLoadSomething) =>
            {
                LocalBundleInfoMgr.SaveData(true);
                if (afterCheckFirstPackage != null)
                {
                    afterCheckFirstPackage(hasLoadSomething);
                }
            }, true, false));
        }

        #endregion 检查首包

        #region 后台下载

        /// <summary>
        /// 检查后台下载
        /// </summary>
        public void CheckBackgroundDownload()
        {
            if (serverBundleInfoMgr == null)
            {
                return;
            }
            StartOrPauseBackgroundDownload(false);
            m_BundleInfosToBackgroundLoad.Clear();
            BundleInfo oldInfo = null;
            foreach (BundleInfo info in serverBundleInfoMgr.InfoList)
            {
                if (info.packageLev > localBundleInfoMgr.m_CurPackageLev)
                {
                    oldInfo = localBundleInfoMgr.InfoList.Find((x) => x.name == info.name);
                    if (oldInfo == null
                        || !oldInfo.hash.Equals(info.hash))
                    {
                        m_BundleInfosToBackgroundLoad.Add(info);
                    }
                }
            }
        }

        /// <summary>
        /// 开始或暂停后台下载
        /// </summary>
        /// <param name="start"></param>
        public void StartOrPauseBackgroundDownload(bool start)
        {
            m_CanBackgroundLoading = start;
        }

        /// <summary>
        /// 某个分包资源是否好了
        /// </summary>
        /// <param name="packageLev"></param>
        /// <returns></returns>
        private bool IsPackageLevOK(int packageLev)
        {
            if (packageLev < 0)
            {
                return true;
            }
            BundleInfo oldInfo = null;
            foreach (BundleInfo info in serverBundleInfoMgr.InfoList)
            {
                if (info.packageLev <= packageLev)
                {
                    oldInfo = localBundleInfoMgr.InfoList.Find((x) => x.name == info.name);
                    if (oldInfo == null
                        || !oldInfo.hash.Equals(info.hash))
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 获取分包需要下载的资源
        /// </summary>
        /// <param name="packageLev"></param>
        /// <returns></returns>
        private List<BundleInfo> GetPackageLevUpdate(int packageLev)
        {
            List<BundleInfo> ret = new List<BundleInfo>();
            BundleInfo oldInfo = null;
            foreach (BundleInfo info in serverBundleInfoMgr.InfoList)
            {
                if (info.packageLev <= packageLev)
                {
                    oldInfo = localBundleInfoMgr.InfoList.Find((x) => x.name == info.name);
                    if (oldInfo == null
                        || !oldInfo.hash.Equals(info.hash))
                    {
                        ret.Add(info);
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// 检查某个分包等级是否OK，如果有下载，会把后台下载停掉
        /// </summary>
        /// <param name="lev">分包等级</param>
        /// <param name="refreshPro">刷新进度回调，参数：进度[0,1]-分子-分母</param>
        /// <param name="afterCheckPackageLevOK">分包等级是否OK回调，参数是是否有加载东西</param>
        public void CheckPackageLevOK(int lev, Action<float, long, long> refreshPro, Action<bool> afterCheckPackageLevOK = null)
        {
            if (m_OnlyUseLocalRes)
            {
                if (afterCheckPackageLevOK != null)
                {
                    afterCheckPackageLevOK(false);
                }
                return;
            }

            if (IsPackageLevOK(lev))
            {
                if (afterCheckPackageLevOK != null)
                {
                    afterCheckPackageLevOK(false);
                }
                return;
            }
            List<BundleInfo> bundles = GetPackageLevUpdate(lev);
            if (bundles.Count <= 0)
            {
                if (afterCheckPackageLevOK != null)
                {
                    afterCheckPackageLevOK(false);
                }
            }
            else
            {
                StartOrPauseBackgroundDownload(false);

                this.StartCoroutine(IE_LoadBundles(bundles, refreshPro, (hasLoadSomething) =>
                {
                    if (afterCheckPackageLevOK != null)
                    {
                        afterCheckPackageLevOK(hasLoadSomething);
                    }
                }, false, false));
            }
        }

        #endregion 后台下载

        #region 加载AssetBundle列表

        /// <summary>
        /// 还将要加载的Bundle
        /// </summary>
        private List<BundleInfo> m_BundlesToLoad = new List<BundleInfo>();
        /// <summary>
        /// 要加载的总量
        /// </summary>
        private long m_BundlesToLoadTotalSize = 0;
        /// <summary>
        /// 加载完成的大小
        /// </summary>
        private long m_BundlesLoadedSize = 0;
        /// <summary>
        /// 加载好的大小
        /// </summary>
        private double m_BundlesLoadedSizeD = 0;
        /// <summary>
        /// 是否需要检查加载好了但是未标记
        /// </summary>
        private bool m_NeedCheckLoadedButNotMark = false;
        /// <summary>
        /// 是否加载完成
        /// </summary>
        private bool m_LoadFinish = true;
        /// <summary>
        /// 是否有真正需要加载的资源
        /// </summary>
        private bool m_HasLoadSomething = false;
        /// <summary>
        /// 加载完成回调，参数是是否加载过东西
        /// </summary>
        private Action<bool> m_LoadBundlesFinishCallback = null;
        /// <summary>
        /// 刷新进度，参数：进度[0,1]-分子-分母
        /// </summary>
        private Action<float, long, long> m_RefreshPro = null;

        /// <summary>
        /// 加载Bundle列表
        /// </summary>
        /// <param name="bundles"></param>
        /// <param name="refreshPro"></param>
        /// <param name="callback"></param>
        /// <param name="fromLocalPath">是否从本地路径，整理首包的时候需要</param>
        /// <param name="tryUnload"></param>
        public void LoadBundles(List<string> bundles, Action<float, long, long> refreshPro, Action<bool> callback, bool fromLocalPath = false, bool tryUnload = false)
        {
            this.StartCoroutine(IE_LoadBundles(localBundleInfoMgr.Names2Infos(bundles), refreshPro, callback, fromLocalPath, tryUnload));
        }

        /// <summary>
        /// 加载Bundle列表
        /// </summary>
        /// <param name="bundles">要加载的bundle列表</param>
        /// <param name="refreshPro">刷新进度回调，参数：进度-分子-分母</param>
        /// <param name="callback">完成回调，是否有资源加载</param>
        /// <param name="fromLocalPath">是否从本地路径，整理首包的时候需要</param>
        /// <param name="tryUnload">是否尝试卸载覆盖，资源更新的时候需要</param>
        /// <returns></returns>
        private IEnumerator IE_LoadBundles(List<BundleInfo> bundles, Action<float, long, long> refreshPro, Action<bool> callback, bool fromLocalPath, bool tryUnload)
        {
            m_LoadBundlesFinishCallback = callback;
            m_RefreshPro = refreshPro;
            m_HasLoadSomething = false;
            m_LoadFinish = false;

            m_BundlesToLoad.Clear();
            m_BundlesToLoad.AddRange(bundles);
            m_BundlesToLoadTotalSize = 0;
            m_BundlesLoadedSize = 0;
            for (int i = 0, imax = bundles.Count; i < imax; i++)
            {
                m_BundlesToLoadTotalSize += bundles[i].size;
            }

            for (int i = 0, imax = bundles.Count; i < imax; i++)
            {
                if (JABMgr.Inst.LoadSingleBundle(bundles[i].name, fromLocalPath, tryUnload))
                {
                    //注意顺序
                    if (m_BundlesToLoad.Contains(bundles[i]))
                    {
                        m_BundlesToLoad.Remove(bundles[i]);
                    }
                    m_BundlesToLoadTotalSize -= bundles[i].size;
                    bundles[i] = null;
                }
                else
                {
                    m_HasLoadSomething = true;
                }
            }
            bundles.RemoveAll((x) => x == null);
            if (m_BundlesToLoadTotalSize < 0)
            {
                m_BundlesToLoadTotalSize = 0;
            }

            while (true)
            {
                if (m_BundlesToLoad.Count <= 0)
                {
                    break;
                }
                RefreshTip();
                yield return Yielders.GetWaitForSeconds(0.1f);
            }

            RefreshTip();
        }

        /// <summary>
        /// Bundle加载完成回调
        /// </summary>
        /// <param name="bundleName"></param>
        /// <param name="success"></param>
        private void BundleLoaded(string bundleName, bool success)
        {
            if (!m_LoadFinish)
            {
                BundleInfo info = m_BundlesToLoad.Find(x => x.name.Equals(bundleName));
                if (info != null)
                {
                    m_BundlesLoadedSize += info.size;
                    m_BundlesToLoad.Remove(info);
                }
            }

            //后台加载的
            if (serverBundleInfoMgr != null)
            {
                localBundleInfoMgr.ChangeOrAddData(serverBundleInfoMgr.InfoList.Find((x) => x.name.Equals(bundleName)), true);
            }
        }

        /// <summary>
        /// 刷新提示
        /// </summary>
        private void RefreshTip()
        {
            if (m_LoadFinish)
            {
                return;
            }

            m_NeedCheckLoadedButNotMark = false;
            m_BundlesLoadedSizeD = (double)m_BundlesLoadedSize;
            for (int i = 0, imax = m_BundlesToLoad.Count; i < imax; i++)
            {
                if (m_BundlesToLoad[i] == null)
                {
                    continue;
                }
                else if (IsBundleLoaded(m_BundlesToLoad[i].name))
                {
                    m_BundlesToLoad[i] = null;//漏网之鱼
                    m_NeedCheckLoadedButNotMark = true;
                }
                else
                {
                    m_BundlesLoadedSizeD += GetBundleLoadProgress(m_BundlesToLoad[i].name) * m_BundlesToLoad[i].size;
                }
            }
            if (m_NeedCheckLoadedButNotMark)
            {
                m_BundlesToLoad.RemoveAll(x => x == null);
            }

            if (m_RefreshPro != null)
            {
                if (m_BundlesToLoadTotalSize > 0)
                {
                    m_RefreshPro((float)m_BundlesLoadedSizeD / m_BundlesToLoadTotalSize, (long)m_BundlesLoadedSizeD, m_BundlesToLoadTotalSize);
                }
                else
                {
                    m_RefreshPro(1f, 1, 1);
                }
            }
            if (m_BundlesToLoad.Count <= 0)
            {
                m_LoadFinish = true;
                m_RefreshPro = null;
                LoadFinish();
            }
        }

        private void LoadFinish()
        {
            if (m_LoadBundlesFinishCallback != null)
            {
                m_LoadBundlesFinishCallback(m_HasLoadSomething);
                m_LoadBundlesFinishCallback = null;
            }
        }

        #endregion 加载AssetBundle列表

        #region 加载AssetBundle

        /// <summary>
        /// 加载AssetBundle和它的依赖
        /// </summary>
        /// <param name="assetBundleName">名称</param>
        /// <param name="isLoadingAssetBundleManifest">是否是加载Manifest</param>
        private void LoadAssetBundle(string assetBundleName, bool isLoadingAssetBundleManifest = false)
        {
            if (!isLoadingAssetBundleManifest)
            {
                if (useManifest && m_AssetBundleManifest == null)
                {
                    Debug.LogError("请先加载Manifest文件");
                    return;
                }
            }
            
            LoadAssetBundleInternal(assetBundleName, isLoadingAssetBundleManifest, false, false, false);
            if (!isLoadingAssetBundleManifest)
            {
                LoadDependencies(assetBundleName);
            }
        }

        /// <summary>
        /// 加载单个bundle，返回是否已经加载好了
        /// </summary>
        /// <param name="assetBundleName">Bundle名称</param>
        /// <param name="fromLocalPath">是否从本地路径，整理首包的时候需要</param>
        /// <param name="tryUnload">如果已存在，尝试卸载，资源更新的时候，要能够覆盖</param>
        /// <returns></returns>
        public bool LoadSingleBundle(string assetBundleName, bool fromLocalPath, bool tryUnload)
        {
            if (string.IsNullOrEmpty(assetBundleName))
            {
                return false;
            }
            return LoadAssetBundleInternal(assetBundleName, false, fromLocalPath, tryUnload, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assetBundleName"></param>
        /// <param name="isLoadingAssetBundleManifest"></param>
        /// <param name="fromLocalPath">是否从本地路径，整理首包的时候需要</param>
        /// <param name="tryUnload">如果已存在，尝试卸载，资源更新的时候，要能够覆盖</param>
        /// <param name="jumpReady">跳过检查准备加载队列，从队列摘取时需要</param>
        /// <returns></returns>
        private bool LoadAssetBundleInternal(string assetBundleName, bool isLoadingAssetBundleManifest, bool fromLocalPath, bool tryUnload, bool jumpReady)
        {
            if (m_PauseLoading
                || string.IsNullOrEmpty(assetBundleName)
                || (!isLoadingAssetBundleManifest && useManifest && m_AssetBundleManifest == null))
            {
                return false;
            }

            // Already loaded.
            AssetBundle bundle = null;
            m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle);
            if (bundle != null)
            {
                if (!tryUnload)
                {
                    return true;
                }
                else
                {
                    UnloadAssetBundle(assetBundleName);
                }
            }

            if (m_DownloadingWWWs.ContainsKey(assetBundleName))
            {
                return true;
            }

            if (!jumpReady)
            {
                ReadyLoadBundle rb = m_ReadyLoadBundles.Find((x) => x.bundleName.Equals(assetBundleName));
                if (rb != null)
                {
                    return true;
                }
            }

            //队列满了，放到Ready
            if (!isLoadingAssetBundleManifest
                && m_DownloadingWWWs.Count >= m_SameTimeLoadBundleCnt)
            {
                m_ReadyLoadBundles.Add(new ReadyLoadBundle()
                {
                    bundleName = assetBundleName,
                    fromLocalPath = fromLocalPath,
                    tryUnload = tryUnload,
                });
            }
            else
            {
                string url = BaseServerDownloadingURL + assetBundleName;
                if (m_OnlyUseLocalRes || fromLocalPath)
                {
                    url = BaseLocalDownloadingURL + assetBundleName;
                }

                WWW www = null;
                if (isLoadingAssetBundleManifest)
                {
                    www = new WWW(url);
                }
                else
                {
                    www = WWW.LoadFromCacheOrDownload(url, GetHashCode(fromLocalPath, assetBundleName), 0);
                }
                m_DownloadingWWWs.Add(assetBundleName, www);
            }
            return false;
        }

        /// <summary>
        /// 检查补齐HashCode，简化使用，HashCode一律走这个接口
        /// </summary>
        /// <param name="fromLocalPath">是否从本地路径，整理首包的时候需要</param>
        /// <param name="assetBundleName">Bundle名称</param>
        /// <returns></returns>
        private Hash128 GetHashCode(bool fromLocalPath, string assetBundleName)
        {
            //最多只会加载一个Manifest文件，本地Manifest文件不一定加载，所以从本地加载时，都靠自己记录的Hash值
            if (fromLocalPath || !useManifest)
            {
                if (fromLocalPath)
                {
                    if (localBundleInfoMgr != null)
                    {
                        BundleInfo info = localBundleInfoMgr.InfoList.Find((x) => x.name == assetBundleName);
                        if (info != null)
                        {
                            return Hash128.Parse(info.hash);
                        }
                    }
                }
                else
                {
                    if (serverBundleInfoMgr != null)
                    {
                        BundleInfo info = serverBundleInfoMgr.InfoList.Find((x) => x.name == assetBundleName);
                        if (info != null)
                        {
                            return Hash128.Parse(info.hash);
                        }
                    }
                }
            }
            else
            {
                return m_AssetBundleManifest.GetAssetBundleHash(assetBundleName);
            }
            return (new Hash128());
        }

        /// <summary>
        /// 获取依赖列表
        /// </summary>
        /// <param name="assetBundleName"></param>
        /// <returns></returns>
        private string[] GetDependencies(string assetBundleName)
        {
            string[] dependencies = null;
            if (useManifest)
            {
                if (m_AssetBundleManifest == null)
                {
                    Debug.LogError("请先加载Manifest文件");
                    return dependencies;
                }
                dependencies = m_AssetBundleManifest.GetAllDependencies(assetBundleName);
            }
            else
            {
                BundleInfo info = null;
                //远端优先
                if (serverBundleInfoMgr != null)
                {
                    info = serverBundleInfoMgr.InfoList.Find((x) => x.name == assetBundleName);
                    if (info != null)
                    {
                        return info.dependencies.ToArray();
                    }
                }
                if (localBundleInfoMgr != null)
                {
                    info = localBundleInfoMgr.InfoList.Find((x) => x.name == assetBundleName);
                    if (info != null)
                    {
                        return info.dependencies.ToArray();
                    }
                }
            }
            return dependencies;
        }

        /// <summary>
        /// <para>加载依赖</para>
        /// </summary>
        /// <param name="assetBundleName"></param>
        private void LoadDependencies(string assetBundleName)
        {
            string[] dependencies = null;
            if (m_Dependencies.ContainsKey(assetBundleName))
            {
                m_Dependencies.TryGetValue(assetBundleName, out dependencies);
            }
            else
            {
                dependencies = GetDependencies(assetBundleName);
            }

            if (dependencies == null || dependencies.Length == 0)
            {
                return;
            }

            for (int i = 0; i < dependencies.Length; i++)
            {
                dependencies[i] = RemapVariantName(dependencies[i]);
            }

            if (m_Dependencies.ContainsKey(assetBundleName))
            {
                m_Dependencies[assetBundleName] = dependencies;
            }
            else
            {
                m_Dependencies.Add(assetBundleName, dependencies);
            }
            for (int i = 0; i < dependencies.Length; i++)
            {
                LoadSingleBundle(dependencies[i], false, false);
            }
        }

        private List<string> keysToRemove = new List<string>();
        private WWW downloadUpdate = null;
        void Update()
        {
            keysToRemove.Clear();
            foreach (var keyValue in m_DownloadingWWWs)
            {
                downloadUpdate = keyValue.Value;

                if (downloadUpdate.error != null)
                {
                    UnityEngine.Debug.LogError(string.Format("Failed download 【{0}】 from {1}: {2}", keyValue.Key, downloadUpdate.url, downloadUpdate.error));
                    if (m_DownloadingErrors.ContainsKey(keyValue.Key))
                    {
                        m_DownloadingErrors[keyValue.Key] = "error";
                    }
                    else
                    {
                        m_DownloadingErrors.Add(keyValue.Key, "error");
                    }

                    keysToRemove.Add(keyValue.Key);
                    if (m_BundleLoadedCallback != null)
                    {
                        m_BundleLoadedCallback(keyValue.Key, false);
                    }
                    DispatchBundleLoaded(keyValue.Key, false);
                    continue;
                }

                // If downloading succeeds.
                if (downloadUpdate.isDone)
                {
                    AssetBundle bundle = downloadUpdate.assetBundle;
                    if (bundle == null)
                    {
                        if (m_DownloadingErrors.ContainsKey(keyValue.Key))
                        {
                            m_DownloadingErrors[keyValue.Key] = string.Format("{0} is not a valid asset bundle.", keyValue.Key);
                        }
                        else
                        {
                            m_DownloadingErrors.Add(keyValue.Key, string.Format("{0} is not a valid asset bundle.", keyValue.Key));
                        }
                        keysToRemove.Add(keyValue.Key);
                        DispatchBundleLoaded(keyValue.Key, false);
                        continue;
                    }

                    //有出现过重复Add的报错，先这样避免一下
                    if (m_LoadedAssetBundles.ContainsKey(keyValue.Key))
                    {
                        m_LoadedAssetBundles[keyValue.Key] = bundle;
                    }
                    else
                    {
                        m_LoadedAssetBundles.Add(keyValue.Key, bundle);
                    }

                    keysToRemove.Add(keyValue.Key);
                    DispatchBundleLoaded(keyValue.Key, true);
                }
            }

            if (keysToRemove.Count > 0)
            {
                // Remove the finished WWWs.
                foreach (string key in keysToRemove)
                {
                    WWW download = m_DownloadingWWWs[key];
                    m_DownloadingWWWs.Remove(key);
                    if (download != null)
                    {
                        download.Dispose();
                        download = null;
                    }
                }

                while (!m_PauseLoading
                    && m_ReadyLoadBundles.Count > 0
                    && m_DownloadingWWWs.Count < m_SameTimeLoadBundleCnt)
                {
                    LoadAssetBundleInternal(m_ReadyLoadBundles[0].bundleName, false, m_ReadyLoadBundles[0].fromLocalPath, m_ReadyLoadBundles[0].tryUnload, true);
                    m_ReadyLoadBundles.RemoveAt(0);
                }
            }

            if (!m_PauseLoading
                && m_CanBackgroundLoading
                && m_BundleInfosToBackgroundLoad.Count > 0
                && m_DownloadingWWWs.Count + m_ReadyLoadBundles.Count < m_BackgroundSameTimeLoadBundleCnt)
            {
                while (m_BundleInfosToBackgroundLoad.Count > 0
                    && m_DownloadingWWWs.Count + m_ReadyLoadBundles.Count < m_BackgroundSameTimeLoadBundleCnt)
                {
                    LoadSingleBundle(m_BundleInfosToBackgroundLoad[0].name, false, false);
                    m_BundleInfosToBackgroundLoad.RemoveAt(0);
                }
            }

            for (int i = 0; i < m_InProgressOperations.Count; )
            {
                if (m_InProgressOperations[i] == null
                    || !m_InProgressOperations[i].Update())
                {
                    m_InProgressOperations[i] = null;
                    m_InProgressOperations.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }
        }

        private void DispatchBundleLoaded(string bundleName, bool success)
        {
            if (m_BundleLoadedCallback != null)
            {
                m_BundleLoadedCallback(bundleName, success);
            }
        }

        /// <summary>
        /// 异步加载，回调形式，不用关心过程
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetBundleName">包含变体</param>
        /// <param name="assetName"></param>
        /// <param name="callback"></param>
        public void LoadAssetAsync<T>(string assetBundleName, string assetName, Action<T> callback) where T : UnityEngine.Object
        {
            if (!CheckLoadArgsAndManifest(assetBundleName, assetName))
            {
                return;
            }

            if (CheckSaveAssets<T>(assetBundleName, assetName, callback))
            {
                return;
            }

            JABMgr.Inst.StartCoroutine(IE_LoadAssetAsync<T>(LoadAssetAsync<T>(assetBundleName, assetName), callback, null));
        }

        /// <summary>
        /// 异步加载，返回IEnumerator，可以自己控制顺序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetBundleName">包含变体</param>
        /// <param name="assetName"></param>
        /// <param name="callback"></param>
        public IEnumerator LoadAssetAsync_IE<T>(string assetBundleName, string assetName, Action<T> callback) where T : UnityEngine.Object
        {
            if (!CheckLoadArgsAndManifest(assetBundleName, assetName))
            {
                return null;
            }

            if (CheckSaveAssets<T>(assetBundleName, assetName, callback))
            {
                return null;
            }

            return IE_LoadAssetAsync<T>(LoadAssetAsync<T>(assetBundleName, assetName), callback, null);
        }

        /// <summary>
        /// <para>异步加载，返回Operation，可以自己控制多个加载的时序</para>
        /// <para>这个不能利用缓存，不对外开放</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetBundleName">包含变体</param>
        /// <param name="assetName"></param>
        /// <returns></returns>
        private JABLoadAssetOperation LoadAssetAsync<T>(string assetBundleName, string assetName) where T : UnityEngine.Object
        {
            if (m_PauseLoading)
            {
                return null;
            }

            assetBundleName = RemapVariantName(assetBundleName);
            LoadAssetBundle(assetBundleName);
            JABLoadAssetOperation operation = new JABLoadAssetOperation(assetBundleName, assetName, typeof(T));

            m_InProgressOperations.Add(operation);

            return operation;
        }

        /// <summary>
        /// 异步加载，回调形式，不用关心过程
        /// </summary>
        /// <param name="assetBundleName">包含变体</param>
        /// <param name="levelName"></param>
        /// <param name="isAdditive"></param>
        /// <param name="callback"></param>
        public void LoadLevelAsync(string assetBundleName, string levelName, bool isAdditive, Action<bool> callback)
        {
            //这里不做参数检查，在下一步会做检查
            JABMgr.Inst.StartCoroutine(IE_LoadAssetAsync<GameObject>(LoadLevelAsync(assetBundleName, levelName, isAdditive), null, callback));
        }

        /// <summary>
        /// 异步加载
        /// </summary>
        /// <param name="assetBundleName">包含变体</param>
        /// <param name="levelName"></param>
        /// <param name="isAdditive"></param>
        /// <returns></returns>
        public JABLoadOperation LoadLevelAsync(string assetBundleName, string levelName, bool isAdditive)
        {
            if (m_PauseLoading)
            {
                return null;
            }

            if (!CheckLoadArgsAndManifest(assetBundleName, levelName))
            {
                return null;
            }

            assetBundleName = RemapVariantName(assetBundleName);
            LoadAssetBundle(assetBundleName);
            JABLoadOperation operation = new JABLoadLevelOperation(assetBundleName, levelName, isAdditive);

            m_InProgressOperations.Add(operation);

            return operation;
        }

        /// <summary>
        /// 检查加载参数和Manifest
        /// </summary>
        /// <param name="assetBundleName">bundle</param>
        /// <param name="assetName">资源名</param>
        /// <returns>是否正常</returns>
        private bool CheckLoadArgsAndManifest(string assetBundleName, string assetName)
        {
            if ((useManifest && m_AssetBundleManifest == null)
                || string.IsNullOrEmpty(assetBundleName)
                || string.IsNullOrEmpty(assetName))
            {
                UnityEngine.Debug.LogError(string.Format("加载资源错误：bundle={0},prefab={1},manifestIsNull={2},useManifest={3}",
                    assetBundleName, assetName, m_AssetBundleManifest == null, useManifest));
                return false;
            }
            return true;
        }

        /// <summary>
        /// 异步加载协程
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request"></param>
        /// <param name="assetCallback">资源加载回调</param>
        /// <param name="levelCallBack">关卡加载回调</param>
        /// <returns></returns>
        private IEnumerator IE_LoadAssetAsync<T>(JABLoadOperation request, Action<T> assetCallback = null, Action<bool> levelCallBack = null) where T : UnityEngine.Object
        {
            if (request == null)
            {
                if (assetCallback != null)
                {
                    assetCallback(null);
                }
                if (levelCallBack != null)
                {
                    levelCallBack(false);
                }
                yield break;
            }
            if (m_PauseLoading)
            {
                yield break;
            }
            yield return JABMgr.Inst.StartCoroutine(request);
            if (m_PauseLoading)
            {
                yield break;
            }
            if (assetCallback != null)
            {
                JABLoadAssetOperation rq = null;
                T asset = null;
                try
                {
                    rq = request as JABLoadAssetOperation;
                    if (rq != null)
                    {
                        asset = rq.GetAsset<T>();
                    }
                }
                catch (Exception)
                {
                }

                if (m_SaveAssets
                    && rq != null
                    && asset != null)
                {
                    if (m_LoadedAssets.ContainsKey(rq.AssetKey))
                    {
                        m_LoadedAssets[rq.AssetKey] = asset;
                    }
                    else
                    {
                        m_LoadedAssets.Add(rq.AssetKey, asset);
                    }
                }
                assetCallback(asset);
            }
            if (levelCallBack != null)
            {
                levelCallBack(true);
            }
        }

        /// <summary>
        /// 检查资源更新
        /// </summary>
        /// <param name="refreshPro"></param>
        /// <param name="afterCheckResUpdate"></param>
        public void CheckResUpdate(Action<float, long, long> refreshPro = null, Action<bool> afterCheckResUpdate = null)
        {
            List<BundleInfo> bundlesToUpdate = new List<BundleInfo>();//待更新的资源

            //这里不检查版本号
            List<BundleInfo> list = new List<BundleInfo>();//整理后的本地列表

            if (serverBundleInfoMgr != null)
            {
                foreach (BundleInfo info in serverBundleInfoMgr.InfoList)
                {
                    BundleInfo oldInfo = localBundleInfoMgr.InfoList.Find((x) => x.name == info.name);
                    if (info.packageLev <= localBundleInfoMgr.m_CurPackageLev)
                    {
                        list.Add(info);

                        if (oldInfo == null
                            || !oldInfo.hash.Equals(info.hash)
                            || oldInfo.packageLev != info.packageLev)
                        {
                            bundlesToUpdate.Add(info);
                        }
                    }
                    else
                    {
                        if (oldInfo != null
                            && oldInfo.hash.Equals(info.hash))
                        {
                            list.Add(info);
                        }
                    }
                }
                localBundleInfoMgr.InfoList = list;
            }

            if (bundlesToUpdate.Count <= 0)
            {
                if (afterCheckResUpdate != null)
                {
                    afterCheckResUpdate(false);
                }
            }
            else
            {
                this.StartCoroutine(IE_LoadBundles(bundlesToUpdate, refreshPro, (hasLoadSomething) =>
                {
                    if (afterCheckResUpdate != null)
                    {
                        afterCheckResUpdate(hasLoadSomething);
                    }
                }, false, true));
            }
        }

        /// <summary>
        /// 加载服务器版本文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="callback"></param>
        private void LoadInfoListFromPath(string path, Action<bool> callback)
        {
            new JerryCoroutine.CorTask(IE_LoadInfoListFromPath(path, callback));
        }

        private IEnumerator IE_LoadInfoListFromPath(string path, Action<bool> callback)
        {
            WWW www = new WWW(path);
            yield return new WaitUntil(() => www.error != null || www.isDone);
            if (!string.IsNullOrEmpty(www.error)
                || string.IsNullOrEmpty(www.text))
            {
                if (callback != null)
                {
                    callback(false);
                }
                yield break;
            }
            serverBundleInfoMgr = JsonUtility.FromJson<BundleInfoMgr>(www.text);
            if (callback != null)
            {
                callback(true);
            }
        }

        /// <summary>
        /// 保存资源更新
        /// </summary>
        public void SaveResUpdate()
        {
            localBundleInfoMgr.m_Version = serverBundleInfoMgr.m_Version;
            localBundleInfoMgr.SaveData(true);
        }

        /// <summary>
        /// 加载Manifest
        /// </summary>
        /// <param name="callback">回调，【是否加载Manifest成功】</param>
        /// <returns></returns>
        private IEnumerator IE_LoadManifest(Action<string> callback)
        {
            yield return new WaitUntil(() => Caching.ready);

            JABLoadManifestOperation request = LoadManifest();
            if (request != null)
            {
                yield return JABMgr.Inst.StartCoroutine(request);
            }
            if (callback != null)
            {
                if (m_AssetBundleManifest != null)
                {
                    callback("");
                }
                else
                {
                    callback("error");
                }
            }
        }

        /// <summary>
        /// 加载Manifest
        /// </summary>
        /// <returns></returns>
        private JABLoadManifestOperation LoadManifest()
        {
            if (m_AssetBundleManifest != null)
            {
                return null;
            }

            LoadAssetBundle(CurPlatformName, true);
            JABLoadManifestOperation operation = new JABLoadManifestOperation(CurPlatformName, "AssetBundleManifest", typeof(AssetBundleManifest));
            m_InProgressOperations.Add(operation);

            return operation;
        }

        #endregion 加载AssetBundle

        #region 卸载AssetBundle

        /// <summary>
        /// 卸载Bundle，切换场景的时候调用
        /// </summary>
        /// <param name="remainBundleNames"></param>
        /// <param name="refreshPro"></param>
        /// <param name="callback"></param>
        public void UnloadAssetBundles(List<string> remainBundleNames, Action<float, long, long> refreshPro, Action callback = null)
        {
            JABMgr.Inst.StartCoroutine(IE_UnloadAssetBundles(remainBundleNames, refreshPro, callback));
        }

        /// <summary>
        /// 卸载Bundle，切换场景的时候调用
        /// </summary>
        /// <param name="remainBundleNames"></param>
        /// <param name="refreshPro"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        private IEnumerator IE_UnloadAssetBundles(List<string> remainBundleNames, Action<float, long, long> refreshPro, Action callback = null)
        {
            //卸载bundle时，如果还有资源在加载，可能出错，停止加载并忽略这些资源

            m_PauseLoading = true;
            StartOrPauseBackgroundDownload(false);
            m_BundleInfosToBackgroundLoad.Clear();

            List<string> names2CheckRemove = new List<string>();//检查要卸载的bundle，先取出来，后面再加载好的就不卸载了
            if (m_LoadedAssetBundles.Keys != null && m_LoadedAssetBundles.Keys.Count > 0)
            {
                names2CheckRemove.AddRange(m_LoadedAssetBundles.Keys);
            }

            m_ReadyLoadBundles.Clear();
            m_LoadedAssets.Clear();

            yield return Yielders.EndOfFrame;

            if (names2CheckRemove.Count <= 0)
            {
                m_PauseLoading = false;
                if (callback != null)
                {
                    callback();
                }
                yield break;
            }

            if (remainBundleNames != null)
            {
                string[] dependencies = null;
                for (int i = 0, imax = remainBundleNames.Count; i < imax; i++)
                {
                    dependencies = m_AssetBundleManifest.GetAllDependencies(remainBundleNames[i]);
                    if (dependencies != null && dependencies.Length > 0)
                    {
                        for (int j = 0, jmax = dependencies.Length; j < jmax; j++)
                        {
                            dependencies[j] = RemapVariantName(dependencies[j]);
                            if (!remainBundleNames.Contains(dependencies[j]))
                            {
                                remainBundleNames.Add(dependencies[j]);
                            }
                        }
                    }
                }
            }

            int toRemoveCnt = names2CheckRemove.Count;
            for (int i = 0; i < toRemoveCnt; i++)
            {
                if (remainBundleNames == null
                    || !remainBundleNames.Contains(names2CheckRemove[i]))
                {
                    UnloadAssetBundle(names2CheckRemove[i]);
                }
                if (refreshPro != null && i % 3 == 0)
                {
                    refreshPro((i + 1) * 1.0f / toRemoveCnt * 1.0f, i + 1, toRemoveCnt);
                    yield return Yielders.EndOfFrame;
                }
            }

            if (refreshPro != null)
            {
                refreshPro(1.0f, toRemoveCnt, toRemoveCnt);
            }
            yield return Yielders.EndOfFrame;

            m_PauseLoading = false;
            if (callback != null)
            {
                callback();
            }
        }

        public void UnloadAssetBundle(string assetBundleName)
        {
            //Manifest不删除
            //正在加载的不能停止或者卸载，随时可能好，会导致重复
            if (string.IsNullOrEmpty(assetBundleName)
                || CurPlatformName.Equals(assetBundleName)
                || m_DownloadingWWWs.ContainsKey(assetBundleName)
                || !m_LoadedAssetBundles.ContainsKey(assetBundleName))
            {
                return;
            }

            AssetBundle bundle = null;
            if (m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle))
            {
                if (bundle != null)
                {
                    bundle.Unload(false);
                    bundle = null;
                }
            }
            m_LoadedAssetBundles.Remove(assetBundleName);
        }

        #endregion 卸载AssetBundle

        #region 辅助

        public void DebugInfo()
        {
            UnityEngine.Debug.Log("加载好的bundle数目:" + m_LoadedAssetBundles.Count);
            List<string> names = new List<string>(m_LoadedAssetBundles.Keys);
            UnityEngine.Debug.Log(JerryUtil.TArray2String<string>(names.ToArray()));
        }

        /// <summary>
        /// Bundle是否加载完成
        /// </summary>
        /// <param name="assetBundleName"></param>
        /// <returns></returns>
        public bool IsBundleLoaded(string assetBundleName)
        {
            if (m_DownloadingErrors.ContainsKey(assetBundleName)
                || m_LoadedAssetBundles.ContainsKey(assetBundleName))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 获取进度
        /// </summary>
        /// <param name="assetBundleName"></param>
        /// <returns></returns>
        public float GetBundleLoadProgress(string assetBundleName)
        {
            if (!m_DownloadingWWWs.ContainsKey(assetBundleName))
            {
                return 0f;
            }
            WWW www = m_DownloadingWWWs[assetBundleName];
            if (www == null || www.isDone)
            {
                return 1f;
            }
            return www.progress;
        }

        /// <summary>
        /// <para>获得加载好的AssetBundle，依赖都加载好了才算好</para>
        /// </summary>
        /// <param name="assetBundleName"></param>
        /// <param name="error"></param>
        /// <returns></returns>
        public AssetBundle GetLoadedAssetBundle(string assetBundleName, out string error)
        {
            if (m_DownloadingErrors.TryGetValue(assetBundleName, out error))
            {
                return null;
            }

            AssetBundle bundle = null;
            m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle);
            if (bundle == null)
            {
                return null;
            }

            // No dependencies are recorded, only the bundle itself is required.
            string[] dependencies = null;
            if (!m_Dependencies.TryGetValue(assetBundleName, out dependencies))
            {
                return bundle;
            }

            // Make sure all dependencies are loaded
            foreach (string dependency in dependencies)
            {
                if (m_DownloadingErrors.TryGetValue(dependency, out error))
                {
                    return null;
                }

                // Wait all the dependent assetBundles being loaded.
                AssetBundle dependentBundle;
                m_LoadedAssetBundles.TryGetValue(dependency, out dependentBundle);
                if (dependentBundle == null)
                {
                    return null;
                }
            }

            return bundle;
        }

        /// <summary>
        /// bundle名检查变体，暂时屏蔽
        /// </summary>
        /// <param name="assetBundleName"></param>
        /// <returns></returns>
        private string RemapVariantName(string assetBundleName)
        {
            return assetBundleName;

            //if (m_AssetBundleManifest == null)
            //{
            //    return assetBundleName;
            //}

            //string[] bundlesWithVariant = m_AssetBundleManifest.GetAllAssetBundlesWithVariant();
            //string[] split = assetBundleName.Split('.');

            //int bestFit = int.MaxValue;
            //int bestFitIndex = -1;
            //for (int i = 0; i < bundlesWithVariant.Length; i++)
            //{
            //    //匹配bundle名
            //    string[] curSplit = bundlesWithVariant[i].Split('.');
            //    if (curSplit[0] != split[0])
            //    {
            //        continue;
            //    }

            //    int found = System.Array.IndexOf(m_ActiveVariants, curSplit[1]);

            //    //如果指定的变体里没有，则使用第一个
            //    if (found == -1)
            //    {
            //        found = int.MaxValue - 1;
            //    }

            //    if (found < bestFit)
            //    {
            //        bestFit = found;
            //        bestFitIndex = i;
            //    }
            //}

            //if (bestFit == int.MaxValue - 1)
            //{
            //    Debug.LogWarning("Ambigious asset bundle variant chosen because there was no matching active variant: " + bundlesWithVariant[bestFitIndex]);
            //}

            //if (bestFitIndex != -1)
            //{
            //    return bundlesWithVariant[bestFitIndex];
            //}
            //else
            //{
            //    return assetBundleName;
            //}
        }

        #endregion 辅助
    }

    public class ReadyLoadBundle
    {
        public string bundleName;
        public bool fromLocalPath;
        public bool tryUnload;
    }
}